﻿using System;
using System.IO.Ports;
using System.Text;
using System.Globalization;
using Newtonsoft.Json.Linq;
using OpenTap.Plugins.Interfaces.PIM;

namespace OpenTap.Plugins.Pim
{
    [Display("Rosenberger Pim Driver", Group: "OpenTap.Plugins", Description: "Rosenberger Pim driver")]
    public class PimRosenberger : Instrument, IPim
    {
        private SerialPort port;

        #region Settings

        [Display("Com port:", Group: "Settings", Order: 1, Description: "Com port to Pim device.")]
        public string ComPort { get; set; }

        #endregion
        public PimRosenberger()
        {
            port = new SerialPort();
            Name = "Rosenberger Pim";
        }

        /// <summary>
        /// Open procedure for the instrument.
        /// </summary>
        public override void Open()
        {
            port.PortName = ComPort;
            port.BaudRate = 9600;
            port.DataBits = 8;
            port.Parity = Parity.None;
            port.StopBits = StopBits.One;
            port.Handshake = Handshake.None;
            //port.NewLine = Convert.ToString('\r');
            port.Open();
            IsConnected = true;
        }

        /// <summary>
        /// Close procedure for the instrument.
        /// </summary>
        public override void Close()
        {
            if (port.IsOpen)
            {
                port.Close();
                IsConnected = false;
            }
            // TODO:  Shut down the connection to the instrument here.
        }

        /// <summary>
        /// Send data to com port. Add '\r' -char end of string.
        /// Throws Exception if command failed...
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        private string SendData(string command)
        {
            string tempCmd = string.Format("{0}{1}", command, Convert.ToString('\r'));

            port.Write(tempCmd);

            // read answer
            port.ReadTimeout = 1000;

            string ret = port.ReadLine();

            ret = ret.Remove(ret.Length - 1, 1);
            if (ret == command)
                throw new Exception("Error!");

            return ret;
        }

        /// <summary>
        /// Converts value into 5 digit format without decimal separator.
        ///  825.5 (Mhz) -> 08255
        /// </summary>
        /// <param name="value">Value to be converted.</param>
        /// <returns></returns>
        private string ConvertTo5Digit(double value)
        {
            string s = value.ToString("0.0");

            s = s.Replace(".", "");
            s = s.Replace(",", "");

            while (s.Length < 5)
                s = s.Insert(0, "0");

            return s;
        }

        /// <inheritdoc />
        public void SetOperationMode(EOperationMode mode)
        {
            string command = "#SL";

            if (mode == EOperationMode.Remote)
                command = "#SR";

            SendData(command);
        }

        /// <inheritdoc />
        public void SetFrequency(EFreqIndex freqIndex, double freq)
        {
            // frequency
            string command = "#SF{0}{1}";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B", ConvertTo5Digit(freq));
            SendData(command);
        }

        /// <inheritdoc />
        public void SetStartFrequency(EFreqIndex freqIndex, double startFreq)
        {
            string command = "#SS{0}{1}";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B", ConvertTo5Digit(startFreq));
            SendData(command);
        }

        /// <inheritdoc />
        public void SetStopFrequency(EFreqIndex freqIndex, double stopFreq)
        {
            string command = "#ST{0}{1}";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B", ConvertTo5Digit(stopFreq));
            SendData(command);
        }

        /// <inheritdoc />
        public void StepSize(double stepSize)
        {
            string command = "#SWA{0}";
            command = string.Format(command, ConvertTo5Digit(stepSize));
            SendData(command);
        }

        /// <inheritdoc />
        public void SetFrequencyBand(EBand eBand)
        {
            string command = "#SAB{0}";
            command = string.Format(command, (ushort) eBand);
            SendData(command);
        }

        /// <inheritdoc />
        public void SetPower(EFreqIndex freqIndex, double power)
        {
            string command = "#SPW{0}{1}";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B", ConvertTo5Digit(power));
            SendData(command);
        }

        /// <inheritdoc />
        public void SetAmplifierOnOff(EFreqIndex freqIndex, bool onOff)
        {
            string command = "#SP{0}{1}";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B", onOff ? 1 : 0);
            SendData(command);
        }

        /// <inheritdoc />
        public void SetModePimMeasurement()
        {
            SendData("SMIM");
        }

        /// <inheritdoc />
        public void SetReflectedPimMeasurement()
        {
            SendData("#SMREF");
        }

        /// <inheritdoc />
        public void SetTransmittedPimMeasurement()
        {
            SendData("#SMTRA");
        }

        /// <inheritdoc />
        public void Set2TonMeasurementOnOff(bool onOff)
        {
            string command = "#M2T{0}";
            command = string.Format(command, onOff ? 1 : 0);
            SendData(command);
        }

        /// <inheritdoc />
        public void UseInternalReceiver(ushort imOrder)
        {
            string command = "#USEID{0}";
            command = string.Format(command, imOrder);
            SendData(command);
        }

        /// <inheritdoc />
        public void Set2TonMeasurementDuration(ushort seconds)
        {
            string command = "#2TTIM,{0}";
            command = string.Format(command, seconds);
            SendData(command);
        }

        /// <inheritdoc />
        public void StartSweepMeasurement()
        {
            SendData("#SWEEP");
        }

        /// <inheritdoc />
        public void SetManuallyVoltageForBothSynthesizers(ushort mV)
        {
            string command = "#RBSAL{0}";
            string voltageString = Convert.ToString(mV);
            voltageString = voltageString.PadLeft(5, '0');
            command = string.Format(command, voltageString);
            SendData(command);
        }

        /// <inheritdoc />
        public void DisableBandSelectButton(bool disable)
        {
            string command = "#BDT{0}";
            command = string.Format(command, disable ? 1 : 0);
            SendData(command);
        }

        /// <inheritdoc />
        public void DisableSweepButton(bool disable)
        {
            string command = "#SWT{0}";
            command = string.Format(command, disable ? 1 : 0);
            SendData(command);
        }

        /// <inheritdoc />
        public void SetInternalReceiverUnit(EReceiverUnit unit)
        {
            string command = "#RECUT{0}";
            command = string.Format(command, unit == EReceiverUnit.dBc ? 0 : 1);
            SendData(command);
        }

        /// <inheritdoc />
        public void SetBaudrate(EBaudRate baudRate)
        {
            string command = "#SETBD,{0}";
            command = string.Format(command, (int)baudRate);
            SendData(command);
        }

        /// <inheritdoc />
        public void SetDetectorMode(EDetectorMode mode)
        {
            string command = "#DET,{0}";
            command = string.Format(command, mode == EDetectorMode.Peak ? "P" : "A");
            SendData(command);
        }

        public string IdnString => SendData("#IDN?");

        /// <inheritdoc />
        public void GetFrequencies(EFreqIndex freqIndez, out double frequency, out double startFrequency, out double stopFrequency)
        {
            string command = "#SF{0}?";
            command = string.Format(command, freqIndez == EFreqIndex.Freq1 ? "A" : "B");
            string ret = SendData(command);
            ret = ret.Substring(3);
            frequency = double.Parse(ret, NumberStyles.Float, CultureInfo.InvariantCulture) / 10.0;

            command = "#SS{0}?";
            command = string.Format(command, freqIndez == EFreqIndex.Freq1 ? "A" : "B");
            ret = SendData(command);
            ret = ret.Substring(3);
            startFrequency = double.Parse(ret, NumberStyles.Float, CultureInfo.InvariantCulture) / 10.0;

            command = "#SS{0}?";
            command = string.Format(command, freqIndez == EFreqIndex.Freq1 ? "A" : "B");
            ret = SendData(command);
            ret = ret.Substring(3);
            stopFrequency = double.Parse(ret, NumberStyles.Float, CultureInfo.InvariantCulture) / 10.0;
        }

        /// <inheritdoc />
        public double GetStepSize()
        {
            string command = "#SWA?";

            string ret = SendData(command);

            ret = ret.Substring(3);

            return (double.Parse(ret, NumberStyles.Float, CultureInfo.InvariantCulture) / 10.0);
        }

        /// <inheritdoc />
        public byte GetStatus()
        {
            string command = "#SST?";
            string ret = SendData(command);
            byte[] bytes = Encoding.ASCII.GetBytes(ret);

            return bytes[3];
        }

        /// <inheritdoc />
        public EBand GetFrequencyBand()
        {
            string command = "#SAB?";

            string ret = SendData(command);

            ret = ret.Substring(3);

            return (EBand) Convert.ToUInt16(ret);
        }

        /// <inheritdoc />
        public double GetPower(EFreqIndex freqIndex)
        {
            string command = "#SPW{0}?";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B");

            string ret = SendData(command);

            //SPWx00402
            ret = ret.Substring(4);
            return double.Parse(ret, NumberStyles.Float, CultureInfo.InvariantCulture) / 10;
        }

        /// <inheritdoc />
        public bool GetAmplifierOnOff(EFreqIndex freqIndex)
        {
            string command = "#SP{0}?";
            command = string.Format(command, freqIndex == EFreqIndex.Freq1 ? "A" : "B");

            string ret = SendData(command);

            //SPA1 SPB1
            return ret.Substring(3, 1) == "1";
        }

        /// <inheritdoc />
        public void GetImOrderAndReceiverLevel(out ushort imOrder, out double receiverLevel)
        {
            string res = SendData("#IMP?");

            //IMP3-085.73dBm
            //    or
            //IMP11-085.73dBc

            int index = res.IndexOf('-');
            string levelStr = res.Substring(index, 6);

            imOrder = Convert.ToUInt16(res.Substring(3, index - 3));

            receiverLevel = double.Parse(levelStr, NumberStyles.Float, CultureInfo.InvariantCulture);
        }

        /// <inheritdoc />
        public double GetReceiverAdcVoltage()
        {
            string res = SendData("#ADC?");

            // ADC04005 (= 400.5 mV)
            string val = res.Substring(3);
            return (double.Parse(val, NumberStyles.Float, CultureInfo.InvariantCulture) / 10);
        }

        public void Initialize(JObject configElement)
        {
            throw new NotImplementedException();
        }
    }
}
